use super::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;

/// Workspace state that needs to be persisted
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceState {
    /// Workspace metadata
    pub metadata: WorkspaceMetadata,
    
    /// Currently opened files and their states
    pub open_files: Vec<FileState>,
    
    /// Window layout configuration
    pub window_layout: WindowLayout,
    
    /// Panel states (sidebar, terminal, etc.)
    pub panels: PanelStates,
    
    /// Editor view states
    pub editor_views: Vec<EditorViewState>,
    
    /// Search and replace history
    pub search_history: SearchHistory,
    
    /// Recently accessed files and folders
    pub recent_items: RecentItems,
    
    /// Workspace-specific settings overrides
    pub settings_overrides: HashMap<String, serde_json::Value>,
    
    /// Extension states for this workspace
    pub extension_states: HashMap<String, serde_json::Value>,
    
    /// Custom workspace data
    pub custom_data: HashMap<String, serde_json::Value>,
}

impl Default for WorkspaceState {
    fn default() -> Self {
        Self {
            metadata: WorkspaceMetadata::default(),
            open_files: Vec::new(),
            window_layout: WindowLayout::default(),
            panels: PanelStates::default(),
            editor_views: Vec::new(),
            search_history: SearchHistory::default(),
            recent_items: RecentItems::default(),
            settings_overrides: HashMap::new(),
            extension_states: HashMap::new(),
            custom_data: HashMap::new(),
        }
    }
}

/// Workspace metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceMetadata {
    /// Workspace name
    pub name: String,
    
    /// Workspace root path
    pub root_path: PathBuf,
    
    /// Last accessed timestamp
    pub last_accessed: chrono::DateTime<chrono::Utc>,
    
    /// Workspace version for migration
    pub version: String,
    
    /// Workspace type (project, folder, etc.)
    pub workspace_type: WorkspaceType,
    
    /// Associated project files (if any)
    pub project_files: Vec<PathBuf>,
}

impl Default for WorkspaceMetadata {
    fn default() -> Self {
        Self {
            name: "Untitled Workspace".to_string(),
            root_path: PathBuf::new(),
            last_accessed: chrono::Utc::now(),
            version: "1.0.0".to_string(),
            workspace_type: WorkspaceType::Folder,
            project_files: Vec::new(),
        }
    }
}

/// Workspace type enumeration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum WorkspaceType {
    /// Single folder workspace
    Folder,
    /// Multi-root workspace
    MultiRoot,
    /// Project-based workspace (with project files)
    Project,
}

/// State of an individual file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileState {
    /// File path relative to workspace root
    pub path: PathBuf,
    
    /// Whether the file is currently open
    pub is_open: bool,
    
    /// Whether the file has unsaved changes
    pub is_dirty: bool,
    
    /// Current cursor position
    pub cursor_position: CursorPosition,
    
    /// Current selection range (if any)
    pub selection: Option<SelectionRange>,
    
    /// Scroll position in the editor
    pub scroll_position: ScrollPosition,
    
    /// Folded regions in the file
    pub folded_regions: Vec<FoldedRegion>,
    
    /// File-specific view settings
    pub view_settings: FileViewSettings,
    
    /// Last modified timestamp when file was opened
    pub last_known_modified: Option<chrono::DateTime<chrono::Utc>>,
}

/// Cursor position in a file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CursorPosition {
    pub line: u32,
    pub column: u32,
}

impl Default for CursorPosition {
    fn default() -> Self {
        Self { line: 0, column: 0 }
    }
}

/// Selection range in a file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectionRange {
    pub start: CursorPosition,
    pub end: CursorPosition,
}

/// Scroll position in the editor
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScrollPosition {
    pub top: u32,
    pub left: u32,
}

impl Default for ScrollPosition {
    fn default() -> Self {
        Self { top: 0, left: 0 }
    }
}

/// Folded region in the editor
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FoldedRegion {
    pub start_line: u32,
    pub end_line: u32,
    pub is_collapsed: bool,
}

/// File-specific view settings
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileViewSettings {
    /// Word wrap enabled for this file
    pub word_wrap: Option<bool>,
    
    /// Line numbers enabled for this file
    pub line_numbers: Option<bool>,
    
    /// Minimap enabled for this file
    pub minimap: Option<bool>,
    
    /// Custom font size for this file
    pub font_size: Option<u32>,
}

impl Default for FileViewSettings {
    fn default() -> Self {
        Self {
            word_wrap: None,
            line_numbers: None,
            minimap: None,
            font_size: None,
        }
    }
}

/// Window layout configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WindowLayout {
    /// Main window dimensions and position
    pub main_window: WindowGeometry,
    
    /// Sidebar configuration
    pub sidebar: SidebarLayout,
    
    /// Panel configuration (bottom panel)
    pub panel: PanelLayout,
    
    /// Editor group layout
    pub editor_groups: Vec<EditorGroup>,
    
    /// Active editor group index
    pub active_editor_group: usize,
}

impl Default for WindowLayout {
    fn default() -> Self {
        Self {
            main_window: WindowGeometry::default(),
            sidebar: SidebarLayout::default(),
            panel: PanelLayout::default(),
            editor_groups: vec![EditorGroup::default()],
            active_editor_group: 0,
        }
    }
}

/// Window geometry (position and size)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WindowGeometry {
    pub x: i32,
    pub y: i32,
    pub width: u32,
    pub height: u32,
    pub is_maximized: bool,
    pub is_fullscreen: bool,
}

impl Default for WindowGeometry {
    fn default() -> Self {
        Self {
            x: 100,
            y: 100,
            width: 1200,
            height: 800,
            is_maximized: false,
            is_fullscreen: false,
        }
    }
}

/// Sidebar layout configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SidebarLayout {
    /// Whether sidebar is visible
    pub is_visible: bool,
    
    /// Sidebar width in pixels
    pub width: u32,
    
    /// Sidebar position (left or right)
    pub position: SidebarPosition,
    
    /// Active sidebar panel
    pub active_panel: String,
    
    /// Panel-specific states
    pub panel_states: HashMap<String, serde_json::Value>,
}

impl Default for SidebarLayout {
    fn default() -> Self {
        Self {
            is_visible: true,
            width: 300,
            position: SidebarPosition::Left,
            active_panel: "explorer".to_string(),
            panel_states: HashMap::new(),
        }
    }
}

/// Panel layout configuration (bottom panel)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PanelLayout {
    /// Whether panel is visible
    pub is_visible: bool,
    
    /// Panel height in pixels
    pub height: u32,
    
    /// Panel position (bottom or right)
    pub position: PanelPosition,
    
    /// Active panel tab
    pub active_tab: String,
    
    /// Tab-specific states
    pub tab_states: HashMap<String, serde_json::Value>,
}

impl Default for PanelLayout {
    fn default() -> Self {
        Self {
            is_visible: false,
            height: 200,
            position: PanelPosition::Bottom,
            active_tab: "terminal".to_string(),
            tab_states: HashMap::new(),
        }
    }
}

/// Editor group (split editor layout)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EditorGroup {
    /// Group identifier
    pub id: String,
    
    /// Open tabs in this group
    pub tabs: Vec<EditorTab>,
    
    /// Active tab index
    pub active_tab: usize,
    
    /// Group dimensions (for split layouts)
    pub dimensions: GroupDimensions,
}

impl Default for EditorGroup {
    fn default() -> Self {
        Self {
            id: "main".to_string(),
            tabs: Vec::new(),
            active_tab: 0,
            dimensions: GroupDimensions::default(),
        }
    }
}

/// Editor tab information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EditorTab {
    /// File path
    pub file_path: PathBuf,
    
    /// Tab title (may differ from filename)
    pub title: String,
    
    /// Whether tab is pinned
    pub is_pinned: bool,
    
    /// Whether tab has unsaved changes
    pub is_dirty: bool,
    
    /// Tab-specific view state
    pub view_state: EditorViewState,
}

/// Group dimensions for split layouts
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroupDimensions {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
}

impl Default for GroupDimensions {
    fn default() -> Self {
        Self {
            x: 0.0,
            y: 0.0,
            width: 1.0,
            height: 1.0,
        }
    }
}

/// Panel states for various UI panels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PanelStates {
    /// Explorer panel state
    pub explorer: ExplorerState,
    
    /// Search panel state
    pub search: SearchPanelState,
    
    /// Git panel state
    pub git: GitPanelState,
    
    /// Extensions panel state
    pub extensions: ExtensionsPanelState,
    
    /// Terminal panel state
    pub terminal: TerminalState,
    
    /// Output panel state
    pub output: OutputPanelState,
    
    /// Problems panel state
    pub problems: ProblemsPanelState,
}

impl Default for PanelStates {
    fn default() -> Self {
        Self {
            explorer: ExplorerState::default(),
            search: SearchPanelState::default(),
            git: GitPanelState::default(),
            extensions: ExtensionsPanelState::default(),
            terminal: TerminalState::default(),
            output: OutputPanelState::default(),
            problems: ProblemsPanelState::default(),
        }
    }
}

/// Explorer panel state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExplorerState {
    /// Expanded folders
    pub expanded_folders: Vec<PathBuf>,
    
    /// Selected items
    pub selected_items: Vec<PathBuf>,
    
    /// Sort order
    pub sort_order: FileSortOrder,
    
    /// Show hidden files
    pub show_hidden: bool,
}

impl Default for ExplorerState {
    fn default() -> Self {
        Self {
            expanded_folders: Vec::new(),
            selected_items: Vec::new(),
            sort_order: FileSortOrder::Name,
            show_hidden: false,
        }
    }
}

/// File sort order options
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FileSortOrder {
    Name,
    Type,
    Size,
    Modified,
}

/// Search panel state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchPanelState {
    /// Current search query
    pub query: String,
    
    /// Replace text
    pub replace_text: String,
    
    /// Search options
    pub options: SearchOptions,
    
    /// Search results
    pub results: Vec<SearchResult>,
}

impl Default for SearchPanelState {
    fn default() -> Self {
        Self {
            query: String::new(),
            replace_text: String::new(),
            options: SearchOptions::default(),
            results: Vec::new(),
        }
    }
}

/// Search options
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchOptions {
    pub case_sensitive: bool,
    pub whole_word: bool,
    pub regex: bool,
    pub include_files: String,
    pub exclude_files: String,
}

impl Default for SearchOptions {
    fn default() -> Self {
        Self {
            case_sensitive: false,
            whole_word: false,
            regex: false,
            include_files: String::new(),
            exclude_files: String::new(),
        }
    }
}

/// Search result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
    pub file_path: PathBuf,
    pub line_number: u32,
    pub column: u32,
    pub match_text: String,
    pub context_before: String,
    pub context_after: String,
}

/// Git panel state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GitPanelState {
    /// Current branch
    pub current_branch: Option<String>,
    
    /// Staged files
    pub staged_files: Vec<PathBuf>,
    
    /// Modified files
    pub modified_files: Vec<PathBuf>,
    
    /// Commit message
    pub commit_message: String,
}

impl Default for GitPanelState {
    fn default() -> Self {
        Self {
            current_branch: None,
            staged_files: Vec::new(),
            modified_files: Vec::new(),
            commit_message: String::new(),
        }
    }
}

/// Extensions panel state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtensionsPanelState {
    /// Search query for extensions
    pub search_query: String,
    
    /// Filter options
    pub filter: ExtensionFilter,
    
    /// Installed extensions
    pub installed_extensions: Vec<String>,
}

impl Default for ExtensionsPanelState {
    fn default() -> Self {
        Self {
            search_query: String::new(),
            filter: ExtensionFilter::All,
            installed_extensions: Vec::new(),
        }
    }
}

/// Extension filter options
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExtensionFilter {
    All,
    Enabled,
    Disabled,
    Installed,
    Popular,
}

/// Terminal state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TerminalState {
    /// Active terminal sessions
    pub sessions: Vec<TerminalSession>,
    
    /// Active session index
    pub active_session: usize,
}

impl Default for TerminalState {
    fn default() -> Self {
        Self {
            sessions: Vec::new(),
            active_session: 0,
        }
    }
}

/// Terminal session
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TerminalSession {
    pub id: String,
    pub name: String,
    pub working_directory: PathBuf,
    pub shell: String,
    pub is_active: bool,
}

/// Output panel state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputPanelState {
    /// Active output channel
    pub active_channel: String,
    
    /// Available channels
    pub channels: Vec<String>,
    
    /// Auto-scroll enabled
    pub auto_scroll: bool,
}

impl Default for OutputPanelState {
    fn default() -> Self {
        Self {
            active_channel: "General".to_string(),
            channels: vec!["General".to_string()],
            auto_scroll: true,
        }
    }
}

/// Problems panel state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProblemsPanelState {
    /// Filter options
    pub filter: ProblemsFilter,
    
    /// Group by file
    pub group_by_file: bool,
    
    /// Show warnings
    pub show_warnings: bool,
    
    /// Show errors
    pub show_errors: bool,
    
    /// Show info messages
    pub show_info: bool,
}

impl Default for ProblemsPanelState {
    fn default() -> Self {
        Self {
            filter: ProblemsFilter::All,
            group_by_file: true,
            show_warnings: true,
            show_errors: true,
            show_info: true,
        }
    }
}

/// Problems filter options
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProblemsFilter {
    All,
    CurrentFile,
    OpenFiles,
}

/// Editor view state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EditorViewState {
    /// File path
    pub file_path: PathBuf,
    
    /// View mode (text, preview, etc.)
    pub view_mode: EditorViewMode,
    
    /// Cursor position
    pub cursor_position: CursorPosition,
    
    /// Selection range
    pub selection: Option<SelectionRange>,
    
    /// Scroll position
    pub scroll_position: ScrollPosition,
    
    /// Folded regions
    pub folded_regions: Vec<FoldedRegion>,
    
    /// View-specific settings
    pub view_settings: FileViewSettings,
}

/// Editor view mode
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EditorViewMode {
    Text,
    Preview,
    Split,
}

/// Search history
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchHistory {
    /// Recent search queries
    pub search_queries: Vec<String>,
    
    /// Recent replace texts
    pub replace_texts: Vec<String>,
    
    /// Maximum history size
    pub max_history_size: usize,
}

impl Default for SearchHistory {
    fn default() -> Self {
        Self {
            search_queries: Vec::new(),
            replace_texts: Vec::new(),
            max_history_size: 50,
        }
    }
}

/// Recent items (files and folders)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecentItems {
    /// Recently opened files
    pub files: Vec<RecentFile>,
    
    /// Recently opened workspaces
    pub workspaces: Vec<RecentWorkspace>,
    
    /// Maximum items to keep
    pub max_items: usize,
}

impl Default for RecentItems {
    fn default() -> Self {
        Self {
            files: Vec::new(),
            workspaces: Vec::new(),
            max_items: 20,
        }
    }
}

/// Recent file information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecentFile {
    pub path: PathBuf,
    pub last_accessed: chrono::DateTime<chrono::Utc>,
    pub workspace_path: Option<PathBuf>,
}

/// Recent workspace information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecentWorkspace {
    pub path: PathBuf,
    pub name: String,
    pub last_accessed: chrono::DateTime<chrono::Utc>,
}

/// Workspace state manager for serialization and persistence
pub struct WorkspaceStateManager {
    storage: Arc<dyn ConfigStorage>,
}

impl WorkspaceStateManager {
    /// Create a new workspace state manager
    pub fn new(storage: Arc<dyn ConfigStorage>) -> Self {
        Self { storage }
    }

    /// Create with file storage for a specific workspace
    pub fn with_workspace_path(workspace_path: &PathBuf) -> Result<Self> {
        let state_dir = workspace_path.join(".codegraph");
        let state_file = state_dir.join("workspace.json");
        let storage = Arc::new(FileConfigStorage::new(state_file)?);
        Ok(Self::new(storage))
    }

    /// Save workspace state
    pub async fn save_state(&self, state: &WorkspaceState) -> Result<()> {
        // Convert WorkspaceState to AppConfig for storage compatibility
        let config_json = serde_json::to_value(state)?;
        let mut app_config = AppConfig::default();
        app_config.custom.insert("workspace_state".to_string(), config_json);

        self.storage.save(&app_config).await
    }

    /// Load workspace state
    pub async fn load_state(&self) -> Result<WorkspaceState> {
        let app_config = self.storage.load().await?;

        if let Some(state_json) = app_config.custom.get("workspace_state") {
            let state: WorkspaceState = serde_json::from_value(state_json.clone())?;
            Ok(state)
        } else {
            Ok(WorkspaceState::default())
        }
    }

    /// Check if workspace state exists
    pub async fn state_exists(&self) -> Result<bool> {
        self.storage.exists().await
    }

    /// Create backup of workspace state
    pub async fn create_backup(&self) -> Result<PathBuf> {
        self.storage.create_backup().await
    }

    /// Export workspace state to JSON string
    pub async fn export_state(&self) -> Result<String> {
        let state = self.load_state().await?;
        Ok(serde_json::to_string_pretty(&state)?)
    }

    /// Import workspace state from JSON string
    pub async fn import_state(&self, json_data: &str) -> Result<()> {
        let state: WorkspaceState = serde_json::from_str(json_data)?;
        self.save_state(&state).await
    }
}

/// Workspace state serialization utilities
pub mod serialization {
    use super::*;
    use std::fs;

    /// Serialize workspace state to JSON bytes
    pub fn serialize_state(state: &WorkspaceState) -> Result<Vec<u8>> {
        let json = serde_json::to_vec_pretty(state)?;
        Ok(json)
    }

    /// Deserialize workspace state from JSON bytes
    pub fn deserialize_state(data: &[u8]) -> Result<WorkspaceState> {
        let state = serde_json::from_slice(data)?;
        Ok(state)
    }

    /// Serialize workspace state to file
    pub fn serialize_to_file(state: &WorkspaceState, file_path: &PathBuf) -> Result<()> {
        let json = serde_json::to_string_pretty(state)?;
        fs::write(file_path, json)?;
        Ok(())
    }

    /// Deserialize workspace state from file
    pub fn deserialize_from_file(file_path: &PathBuf) -> Result<WorkspaceState> {
        let content = fs::read_to_string(file_path)?;
        let state = serde_json::from_str(&content)?;
        Ok(state)
    }

    /// Validate workspace state structure
    pub fn validate_state(state: &WorkspaceState) -> ValidationResult {
        let mut errors = Vec::new();
        let mut warnings = Vec::new();

        // Validate metadata
        if state.metadata.name.is_empty() {
            warnings.push("Workspace name is empty".to_string());
        }

        if !state.metadata.root_path.exists() {
            warnings.push("Workspace root path does not exist".to_string());
        }

        // Validate open files
        for file_state in &state.open_files {
            if !file_state.path.is_absolute() && !state.metadata.root_path.join(&file_state.path).exists() {
                warnings.push(format!("File does not exist: {}", file_state.path.display()));
            }
        }

        // Validate editor groups
        if state.window_layout.editor_groups.is_empty() {
            errors.push("At least one editor group is required".to_string());
        }

        if state.window_layout.active_editor_group >= state.window_layout.editor_groups.len() {
            errors.push("Active editor group index is out of bounds".to_string());
        }

        ValidationResult {
            is_valid: errors.is_empty(),
            errors,
            warnings,
        }
    }

    /// Migrate workspace state to current version
    pub fn migrate_state(state: &mut WorkspaceState) -> Result<()> {
        // Check if migration is needed
        let current_version = semver::Version::parse("1.0.0")?;
        let state_version = semver::Version::parse(&state.metadata.version)?;

        if state_version < current_version {
            // Perform migration
            state.metadata.version = "1.0.0".to_string();

            // Add any migration logic here for future versions
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    #[test]
    fn test_workspace_state_serialization() {
        let state = WorkspaceState::default();

        // Test JSON serialization
        let json = serde_json::to_string(&state).unwrap();
        let deserialized: WorkspaceState = serde_json::from_str(&json).unwrap();

        assert_eq!(state.metadata.version, deserialized.metadata.version);
        assert_eq!(state.open_files.len(), deserialized.open_files.len());
    }

    #[test]
    fn test_workspace_state_validation() {
        let mut state = WorkspaceState::default();
        state.metadata.name = "Test Workspace".to_string();

        let result = serialization::validate_state(&state);
        assert!(result.is_valid);
        assert!(result.errors.is_empty());
    }

    #[tokio::test]
    async fn test_workspace_state_manager() {
        let temp_dir = TempDir::new().unwrap();
        let workspace_path = temp_dir.path().to_path_buf();

        let manager = WorkspaceStateManager::with_workspace_path(&workspace_path).unwrap();

        let mut state = WorkspaceState::default();
        state.metadata.name = "Test Workspace".to_string();
        state.metadata.root_path = workspace_path.clone();

        // Test saving and loading
        manager.save_state(&state).await.unwrap();
        let loaded_state = manager.load_state().await.unwrap();

        assert_eq!(state.metadata.name, loaded_state.metadata.name);
        assert_eq!(state.metadata.root_path, loaded_state.metadata.root_path);
    }
}
